/* This file defines the kexception function which manages the interrupts in our system.
   It takes a cause_reg_t variable containing the value of the cause register as parameter.
*/
#include "pcb.h"
#include "scheduler.h"
#include "types.h"
#include "mips4kc.h"
#include "malta.h"
#include "timer.h"
#include "null.h"
#include "syscall.h"
#include "uart.h"

cause_reg_t cause_reg; 

void kexception(){
  uint8_t exc = cause_reg.field.exc;
  uint8_t ip = cause_reg.field.ip;
  switch(exc){
  case 0:
    //external interrupt
    if(ip & 0x80){
      // timer interrupt
      // we need to check if any delayed process needs to be waken up */
      kset_timer(TIME_QUANTUM);
      process_delayed();
      schedule();
      kset_cause(~0x8000,0); //clear the flag for timer interrupt
    }else if(ip & 4){
      //tty0 interrupt
      uart_handler();
      kset_cause(~0x1000,0);                         
    }
    break;
    
  case 8:
    /*syscall interrupt, the syscall code is in v0, parameters in a-registers
      we first move the code into the global variable in the syscall module
      and we call the dispatch_syscall function. The return value of dispatch_syscall must be explicitly saved in the PCB of the caller to be restored properly (using the caller global variable where the a pointer to the caller's PCB has been saved before treating the system call) */
    asm(
	"sw $k0, caller\n\t" //$k0 is a pointer to the caller's PCB at that step
	);      
    /* call to the syscall handler */
    dispatch_syscall();
    /* acknowledge system call */
    kset_cause(~0x60, 0);
    /* right now, the saved value of the EPC points to the syscall exception. We need to make it point to the next instruction */
    pcb_set_epc(caller,pcb_get_epc(caller)+4);
    break;
  }
    
}

